package aiku.numericalcontroloa.Service.EmployeeAttendance.Impl;

import aiku.numericalcontroloa.Base.Utils.BaseEnum;
import aiku.numericalcontroloa.Base.Utils.HttpStatus;
import aiku.numericalcontroloa.Base.Utils.Result;
import aiku.numericalcontroloa.Mapper.DepartmentManagement.DepartmentMapper;
import aiku.numericalcontroloa.Mapper.DepartmentManagement.EmployeeDepartmentMapper;
import aiku.numericalcontroloa.Mapper.EmployeeAttendance.AttendanceApplyMapper;
import aiku.numericalcontroloa.Mapper.EmployeeAttendance.AttendanceMapper;
import aiku.numericalcontroloa.Mapper.EmployeeAttendance.ClockinRuleMapper;
import aiku.numericalcontroloa.Mapper.MasterControl.MessageNotificationMapper;
import aiku.numericalcontroloa.Mapper.UserMapper;
import aiku.numericalcontroloa.Model.Dto.EmployeeAttendance.AttendanceApplyDto;
import aiku.numericalcontroloa.Model.Entity.DepartmentManagement.Department;
import aiku.numericalcontroloa.Model.Entity.DepartmentManagement.EmployeeDepartment;
import aiku.numericalcontroloa.Model.Entity.EmployeeAttendance.Attendance;
import aiku.numericalcontroloa.Model.Entity.EmployeeAttendance.AttendanceApply;
import aiku.numericalcontroloa.Model.Entity.EmployeeAttendance.ClockinRule;
import aiku.numericalcontroloa.Model.Entity.MasterControl.MessageNotification;
import aiku.numericalcontroloa.Model.Entity.User;
import aiku.numericalcontroloa.Model.Vo.EmployeeAttendance.AttendanceApplyVo;
import aiku.numericalcontroloa.Service.EmployeeAttendance.AttendanceApplyService;
import aiku.numericalcontroloa.common.utils.DateUtils;
import aiku.numericalcontroloa.enums.RoleEnum;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Time;
import java.time.LocalDate;
import java.time.Year;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author HuangGuangTing
 * @Date 2024/6/21 13:37
 */
@Service
public class AttendanceApplyServiceImpl implements AttendanceApplyService {
    private final AttendanceMapper attendanceMapper;
    private final AttendanceApplyMapper attendanceApplyMapper;
    private final UserMapper userMapper;
    private final ClockinRuleMapper clockinRuleMapper;
    private final DepartmentMapper departmentMapper;
    private final EmployeeDepartmentMapper employeeDepartmentMapper;
    private final MessageNotificationMapper  msgNotificationMapper;

    public AttendanceApplyServiceImpl(AttendanceMapper attendanceMapper, AttendanceApplyMapper attendanceApplyMapper, UserMapper userMapper, ClockinRuleMapper clockinRuleMapper, DepartmentMapper departmentMapper, EmployeeDepartmentMapper employeeDepartmentMapper, MessageNotificationMapper msgNotificationMapper) {
        this.attendanceMapper = attendanceMapper;
        this.attendanceApplyMapper = attendanceApplyMapper;
        this.userMapper = userMapper;
        this.clockinRuleMapper = clockinRuleMapper;
        this.departmentMapper = departmentMapper;
        this.employeeDepartmentMapper = employeeDepartmentMapper;
        this.msgNotificationMapper = msgNotificationMapper;
    }

    @Override
    @Transactional
    public Result addAttendanceApply(AttendanceApplyDto attendanceApplyDto) {
        if (attendanceApplyDto == null){
            return new Result(HttpStatus.BAD_REQUEST,"缺少参数");
        }
        User user = userMapper.selectById(StpUtil.getLoginIdAsString());
        AttendanceApply attendanceApply = new AttendanceApply();
        // 申请人
        attendanceApply.setApplicantId(user.getId());
        if (attendanceApplyDto.getApplyType() != 1){
            // 开始时间
            if (attendanceApplyDto.getStartTime() == null){
                return new Result(HttpStatus.BAD_REQUEST,"缺少参数(StartTime为空)");
            }
            attendanceApply.setStartTime(DateUtils.YMDHMSFormatDate(attendanceApplyDto.getStartTime()));
            // 结束时间
            if (attendanceApplyDto.getStopTime() == null){
                return new Result(HttpStatus.BAD_REQUEST,"缺少参数(StopTime为空)");
            }
            attendanceApply.setStopTime(DateUtils.YMDHMSFormatDate(attendanceApplyDto.getStopTime()));
        }

        // 申请事由
        if (attendanceApplyDto.getApplyExplain() != null && !attendanceApplyDto.getApplyExplain().isEmpty()){
            attendanceApply.setApplyExplain(attendanceApplyDto.getApplyExplain());
        }
        // 附件
        if (attendanceApplyDto.getAccessoryUrl() != null && !attendanceApplyDto.getAccessoryUrl().isEmpty()){
            attendanceApply.setAccessoryUrl(attendanceApplyDto.getAccessoryUrl());
        }
        // 地点
        if (attendanceApplyDto.getLocation() != null && !attendanceApplyDto.getLocation().isEmpty()){
            attendanceApply.setLocation(attendanceApplyDto.getLocation());
        }
        // 审批状态设置为待审核
        attendanceApply.setApprovalStatus(0);
        // 申请类型
        if (attendanceApplyDto.getApplyType() == null){
            return new Result(HttpStatus.BAD_REQUEST,"缺少参数(ApplyType为空)");
        }
        attendanceApply.setApplyType(attendanceApplyDto.getApplyType());
        if (attendanceApply.getApplyType() == 0){
            // 类型为请假
            // 请假类型
            if (attendanceApplyDto.getVacateType() == null){
                return new Result(HttpStatus.BAD_REQUEST,"缺少参数(VacateType为空)");
            }
            attendanceApply.setVacateType(attendanceApplyDto.getVacateType());
        }else if (attendanceApply.getApplyType() == 1){
            // 类型为补卡
            // 考勤id
            if (attendanceApplyDto.getAttendanceId() == null || attendanceApplyDto.getAttendanceId().isEmpty()){
                return new Result(HttpStatus.BAD_REQUEST,"缺少参数(AttendanceId为空)");
            }
            if (attendanceApplyDto.getAttendanceLot() == null){
                return new Result(HttpStatus.BAD_REQUEST,"缺少参数(AttendanceLot为空)");
            }
            Attendance attendance = attendanceMapper.selectById(attendanceApplyDto.getAttendanceId());
            if (attendance == null){
                return new Result(HttpStatus.BAD_REQUEST,"该出勤记录不存在");
            }
            // 考勤id
            attendanceApply.setAttendanceId(attendanceApplyDto.getAttendanceId());
            // 考勤批次
            attendanceApply.setAttendanceLot(attendanceApplyDto.getAttendanceLot());
        }else {
            // 类型为外勤，出差
            if (attendanceApplyDto.getLocation() == null || attendanceApplyDto.getLocation().isEmpty()){
                return new Result(HttpStatus.BAD_REQUEST,"缺少参数(Location为空)");
            }
            attendanceApply.setAttendanceLot(attendanceApplyDto.getAttendanceLot());
        }
        attendanceApplyMapper.insert(attendanceApply);

        // 查管理员角色
        LambdaQueryWrapper<User> userLQW = new LambdaQueryWrapper<>();
        userLQW.eq(User::getRoleId,RoleEnum.ROLE_1.getCodeToString())
                .eq(User::getState,BaseEnum.USER_INCUMBENCY.getCodeInt());
        List<User> userList = userMapper.selectList(userLQW);
        if (userList.isEmpty()){
            return Result.success("申请成功");
        }
        List<MessageNotification> msgList = new ArrayList<>();
        for (User admin : userList){
            // 创建消息记录
            MessageNotification msg = new MessageNotification();
            msg.setId(UUID.randomUUID().toString().replaceAll("-",""));
            LocalDate startTime = LocalDate.now();
            LocalDate stopTime = LocalDate.now();
            if (attendanceApply.getStartTime() != null){
                startTime = attendanceApply.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            }
            if (attendanceApply.getStopTime() != null){
                stopTime = attendanceApply.getStopTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            }
            // 标题
            String applyType;
            String content;
            if (attendanceApply.getApplyType() == 0){
                // 请假
                applyType = "请假";
                content =  user.getNickName()+"申请"+startTime.getMonthValue()+"月"+startTime.getDayOfMonth()+"日到"+stopTime.getMonthValue()+"月"+stopTime.getDayOfMonth()+"日"+applyType;
            }else if (attendanceApply.getApplyType() == 1){
                // 补卡
                applyType = "补卡";
                content =  user.getNickName()+"申请补卡";
            }else if (attendanceApply.getApplyType() == 2){
                // 外勤
                applyType = "外勤";
                content =  user.getNickName()+"申请"+startTime.getMonthValue()+"月"+startTime.getDayOfMonth()+"日到"+stopTime.getMonthValue()+"月"+stopTime.getDayOfMonth()+"日"+applyType;
            }else {
                // 出差
                applyType = "出差";
                content =  user.getNickName()+"申请"+startTime.getMonthValue()+"月"+startTime.getDayOfMonth()+"日到"+stopTime.getMonthValue()+"月"+stopTime.getDayOfMonth()+"日"+applyType;
            }
            String title = "有新的"+applyType+"申请待审批";
            msg.setTitle(title);
            // 内容
            msg.setContent(content);
            // 发布人为申请人
            msg.setPublisherId(user.getId());
            // 接收人为管理员
            msg.setReceiverId(admin.getId());
            // 关联id
            msg.setRelevanceId(attendanceApply.getId());
            // 类型为员工考勤
            msg.setMsgType(3);
            // 子菜单为5考勤审批
            msg.setSubmenu(5);
            // 状态为待处理
            msg.setStatus(0);
            msg.setCreateTime(new Date());
            msgList.add(msg);
        }
        msgNotificationMapper.insertBatch(msgList);
        return Result.success("申请成功");
    }

    @Override
    public Result getAttendanceApplyList(AttendanceApplyDto attendanceApplyDto) {
        if (attendanceApplyDto == null){
            return new Result(HttpStatus.BAD_REQUEST,"缺少参数");
        }
        if (attendanceApplyDto.getCurrentPage() == null || attendanceApplyDto.getPageSize() == null){
            return new Result(HttpStatus.BAD_REQUEST,"缺少参数");
        }

        User loginUser = userMapper.selectById(StpUtil.getLoginIdAsString());
        // 管理员，厂长审批
        boolean root = loginUser.getRoleId().equals(RoleEnum.ROLE_1.getCodeToString()) ||
                loginUser.getRoleId().equals(RoleEnum.ROLE_5.getCodeToString());

        Page<AttendanceApply> applyPage = new Page<>(attendanceApplyDto.getCurrentPage(),attendanceApplyDto.getPageSize());
        LambdaQueryWrapper<AttendanceApply> applyLQW = new LambdaQueryWrapper<>();
        applyLQW.orderByDesc(AttendanceApply::getCreateTime).eq(AttendanceApply::getApplyType,attendanceApplyDto.getApplyType());
        // 条件查询
        // 按申请人查询
        if (root && attendanceApplyDto.getApplicantId() != null && !attendanceApplyDto.getApplicantId().isEmpty()){
            applyLQW.eq(AttendanceApply::getApplicantId,attendanceApplyDto.getApplicantId());
        }else if (!root){
            applyLQW.eq(AttendanceApply::getApplicantId,loginUser.getId());
        }
        // 按年份查询
        if (attendanceApplyDto.getYear() != null){
            applyLQW.apply("YEAR(create_time) = {0}",attendanceApplyDto.getYear());
        }else {
            // 默认按当前年查询
            applyLQW.apply("YEAR(create_time) = {0}", Year.now());
        }
        // 按审批状态查询
        if (attendanceApplyDto.getApprovalStatus() != null){
            applyLQW.eq(AttendanceApply::getApprovalStatus,attendanceApplyDto.getApprovalStatus());
        }
        List<AttendanceApply> applyList = attendanceApplyMapper.selectPage(applyPage, applyLQW).getRecords();
        Map<String,Object> map = new HashMap<>();
        List<AttendanceApplyVo> applyVoList = new ArrayList<>();
        if (applyList.isEmpty()){
            map.put("total",0);
            map.put("applyVoList",applyVoList);
            return Result.success("查询成功",map);
        }

        // 根据申请人id列表查询用户
        Set<String> userIdList = applyList.stream().map(AttendanceApply::getApplicantId).collect(Collectors.toSet());
        Set<String> approvalIdList = applyList.stream().map(AttendanceApply::getApprovalId).collect(Collectors.toSet());
        userIdList.addAll(approvalIdList);
        List<User> userList = userMapper.selectBatchByIdWithDelete(userIdList);
        Map<String, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));

        // 根据出勤id列表查出勤记录
        List<String> attendanceIdList = applyList.stream()
                .filter(apply -> apply.getAttendanceId() != null && !apply.getAttendanceId().isEmpty())
                .map(AttendanceApply::getAttendanceId)
                .collect(Collectors.toList());
        Map<String, Attendance> attendanceMap = new HashMap<>();
        Map<String, ClockinRule> clockinRuleMap = new HashMap<>();
        if (attendanceIdList.size() > 0){
            List<Attendance> attendanceList = attendanceMapper.selectBatchIds(attendanceIdList);
            // 根据出勤id获取出勤记录
            attendanceMap = attendanceList.stream().collect(Collectors.toMap(Attendance::getId,Function.identity()));
            // 获取打卡规则id集合
            Set<String> clockinRuleIdList = attendanceList.stream().map(Attendance::getClockinRuleId).collect(Collectors.toSet());
            // 根据打卡规则id集合查询打卡规则记录
            List<ClockinRule> clockinRuleList = clockinRuleMapper.selectBatchByIdWithDelete(clockinRuleIdList);
            // 处理成map，根据出勤id获取打卡规则
            clockinRuleMap = attendanceList.stream().collect(Collectors.toMap(Attendance::getId, attendance -> clockinRuleList.stream()
                    .filter(clockinRule -> clockinRule.getId().equals(attendance.getClockinRuleId()))
                    .findFirst()
                    .orElse(null)));
        }

        for (AttendanceApply attendanceApply : applyList){
            User user = userMap.get(attendanceApply.getApplicantId());
            AttendanceApplyVo applyVo = new AttendanceApplyVo();
            BeanUtils.copyProperties(attendanceApply,applyVo);
            // 申请人
            applyVo.setNickName(user.getNickName());
            // 开始时间
            if (attendanceApply.getStartTime() != null){
                applyVo.setStartTime(DateUtils.YMDHMSFormat(attendanceApply.getStartTime()));
            }
            // 结束时间
            if (attendanceApply.getStopTime() != null){
                applyVo.setStopTime(DateUtils.YMDHMSFormat(attendanceApply.getStopTime()));
            }
            // 审核人
            if (attendanceApply.getApprovalId() != null){
                User approval = userMap.get(attendanceApply.getApprovalId());
                applyVo.setApprovalName(approval.getNickName());
            }
            // 审核时间
            if (attendanceApply.getApprovalTime() != null){
                applyVo.setApprovalTime(DateUtils.YMDHMSFormat(attendanceApply.getApprovalTime()));
            }
            // 补卡
            if (attendanceApply.getApplyType() == 1){
                // 获取出勤记录
                Attendance attendance = attendanceMap.get(attendanceApply.getAttendanceId());
                // 设置补卡日期
                applyVo.setClockingDate(attendance.getClockingDate());
                // 获取打卡规则
                ClockinRule clockinRule = clockinRuleMap.get(attendanceApply.getAttendanceId());
                if (attendanceApply.getAttendanceLot() == 0){
                    // 上午签到
                    applyVo.setStringLot("上午签到("+clockinRule.getForenoonCheckinTime()+")");
                }else if (attendanceApply.getAttendanceLot() == 1){
                    // 上午签退
                    applyVo.setStringLot("上午签退("+clockinRule.getForenoonCheckoutTime()+")");
                }else if (attendanceApply.getAttendanceLot() == 2){
                    // 下午签到
                    applyVo.setStringLot("下午签到("+clockinRule.getAfternoonCheckinTime()+")");
                }else if (attendanceApply.getAttendanceLot() == 3){
                    // 下午签退
                    applyVo.setStringLot("下午签退("+clockinRule.getAfternoonCheckoutTime()+")");
                }
            }
            // 申请日期
            applyVo.setCreateDate(attendanceApply.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
            applyVoList.add(applyVo);
        }
        map.put("total",applyPage.getTotal());
        map.put("applyVoList",applyVoList);
        return Result.success("查询成功",map);
    }

    @Override
    public Result getAttendanceApply(AttendanceApplyDto attendanceApplyDto) {
        if (attendanceApplyDto == null){
            return new Result(HttpStatus.BAD_REQUEST,"缺少参数");
        }
        if (attendanceApplyDto.getId() == null || attendanceApplyDto.getId().isEmpty()){
            return new Result(HttpStatus.BAD_REQUEST,"缺少参数(attendanceApplyDto.id为空)");
        }
        AttendanceApply attendanceApply = attendanceApplyMapper.selectById(attendanceApplyDto.getId());
        if (attendanceApply == null){
            return new Result(HttpStatus.BAD_REQUEST,"该申请为空");
        }
        AttendanceApplyVo applyVo = new AttendanceApplyVo();
        BeanUtils.copyProperties(attendanceApply,applyVo);
        // 查申请人
        User user = userMapper.selectById(attendanceApply.getApplicantId());
        applyVo.setNickName(user.getNickName());
        // 开始时间
        if (attendanceApply.getStartTime() != null){
            applyVo.setStartTime(DateUtils.YMDHMSFormat(attendanceApply.getStartTime()));
        }
        // 结束时间
        if (attendanceApply.getStopTime() != null){
            applyVo.setStopTime(DateUtils.YMDHMSFormat(attendanceApply.getStopTime()));
        }
        // 审核人
        if (attendanceApply.getApprovalId() != null){
            User approval = userMapper.selectById(attendanceApply.getApprovalId());
            applyVo.setApprovalName(approval.getNickName());
        }
        // 审核时间
        if (attendanceApply.getApprovalTime() != null){
            applyVo.setApprovalTime(DateUtils.YMDHMSFormat(attendanceApply.getApprovalTime()));
        }
        // 补卡
        if (attendanceApply.getApplyType() == 1){
            // 获取出勤记录
            Attendance attendance = attendanceMapper.selectById(attendanceApply.getAttendanceId());
            // 设置补卡日期
            applyVo.setClockingDate(attendance.getClockingDate());
            // 获取打卡规则
            ClockinRule clockinRule = clockinRuleMapper.selectById(attendance.getClockinRuleId());
            if (attendanceApply.getAttendanceLot() == 0){
                // 上午签到
                applyVo.setStringLot("上午签到("+clockinRule.getForenoonCheckinTime()+")");
            }else if (attendanceApply.getAttendanceLot() == 1){
                // 上午签退
                applyVo.setStringLot("上午签退("+clockinRule.getForenoonCheckoutTime()+")");
            }else if (attendanceApply.getAttendanceLot() == 2){
                // 下午签到
                applyVo.setStringLot("下午签到("+clockinRule.getAfternoonCheckinTime()+")");
            }else if (attendanceApply.getAttendanceLot() == 3){
                // 下午签退
                applyVo.setStringLot("下午签退("+clockinRule.getAfternoonCheckoutTime()+")");
            }
        }
        // 申请日期
        applyVo.setCreateDate(attendanceApply.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
        return Result.success("查询成功",applyVo);
    }

    @Override
    @Transactional
    public Result examineAttendanceApply(AttendanceApplyDto attendanceApplyDto) {
        if (attendanceApplyDto == null){
            return new Result(HttpStatus.BAD_REQUEST,"缺少参数(attendanceApplyDto为null)");
        }
        if (attendanceApplyDto.getId() == null || attendanceApplyDto.getId().isEmpty()){
            return new Result(HttpStatus.BAD_REQUEST,"缺少参数(id为null)");
        }
        AttendanceApply attendanceApply = attendanceApplyMapper.selectById(attendanceApplyDto.getId());
        if (attendanceApply == null){
            return new Result(HttpStatus.BAD_REQUEST,"该出勤申请不存在");
        }
        // 管理员和厂长有权
        User user = userMapper.selectById(StpUtil.getLoginIdAsString());
        boolean root = user.getRoleId().equals(RoleEnum.ROLE_1.getCodeToString()) ||
                user.getRoleId().equals(RoleEnum.ROLE_5.getCodeToString());
        if (!root){
            return new Result(HttpStatus.BAD_REQUEST,"没有权限操作");
        }
        attendanceApply.setApprovalId(user.getId());
        attendanceApply.setApprovalTime(new Date());
        attendanceApply.setApprovalStatus(attendanceApplyDto.getApprovalStatus());

        if (attendanceApply.getApplyType() == 0 && attendanceApply.getApprovalStatus() == 1){
            // 请假通过
            // 获取请假开始日期
            LocalDate startTime = attendanceApply.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            // 获取请假结束日期
            LocalDate stopTime = attendanceApply.getStopTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            // 获取开始日期到结束日期的出勤记录
            LambdaQueryWrapper<Attendance> attendanceLQW = new LambdaQueryWrapper<>();
            attendanceLQW.eq(Attendance::getEmployeeId,attendanceApply.getApplicantId())
                    .between(Attendance::getClockingDate,startTime,stopTime);
            List<Attendance> attendanceList = attendanceMapper.selectList(attendanceLQW);
            // 获取打卡规则
            // 先查询个人打卡规则
            LambdaQueryWrapper<ClockinRule> clockinRuleLQW = new LambdaQueryWrapper<>();
            clockinRuleLQW.eq(ClockinRule::getEmployeeId,attendanceApply.getApplicantId())
                    .eq(ClockinRule::getRuleType,0);
            ClockinRule clockinRule = clockinRuleMapper.selectOne(clockinRuleLQW);
            // 没有个人打卡规则，就查询部门打卡规则
            if (clockinRule == null || clockinRule.getForenoonCheckinTime() == null){
                LambdaQueryWrapper<EmployeeDepartment> employeeDepartmentLQW = new LambdaQueryWrapper<>();
                employeeDepartmentLQW.eq(EmployeeDepartment::getEmployeeId,attendanceApply.getApplicantId());
                List<EmployeeDepartment> employeeDepartmentList = employeeDepartmentMapper.selectList(employeeDepartmentLQW);
                if (employeeDepartmentList.size() == 1){
                    clockinRule = getDepartmentClockinRule(employeeDepartmentList.get(0).getDepartmentId());
                }else{
                    String msg = "请联系管理员设置个人打卡规则";
                    return new Result(HttpStatus.NO_CONTENT,msg);
                }
            }
            if (clockinRule == null){
                String msg = "请联系管理员设置个人打卡规则";
                return new Result(HttpStatus.NO_CONTENT,msg);
            }
            Time forenoonCheckinTime = clockinRule.getForenoonCheckinTime();
            Time forenoonCheckoutTime = clockinRule.getForenoonCheckoutTime();
            Time afternoonCheckinTime = clockinRule.getAfternoonCheckinTime();
            Time afternoonCheckoutTime = clockinRule.getAfternoonCheckoutTime();
            Time newStartTime = new Time(attendanceApply.getStartTime().getTime());
            Time newStopTime = new Time(attendanceApply.getStopTime().getTime());
            if (attendanceList.isEmpty()){
                // 请假开始日期的请假记录为空就新建
                long daysBetween = ChronoUnit.DAYS.between(startTime, stopTime);
                if (daysBetween > 0){
                    // 请多天
                    List<Attendance> newAttendanceList = new ArrayList<>();
                    for (int i = 0; i <= daysBetween; i++) {
                        LocalDate clockingDate = startTime.plusDays(i);
                        Attendance newAttendance = new Attendance();
                        newAttendance.setId(UUID.randomUUID().toString().replaceAll("-", ""));
                        newAttendance.setEmployeeId(attendanceApply.getApplicantId());
                        newAttendance.setClockinRuleId(clockinRule.getId());
                        newAttendance.setClockingDate(clockingDate);
                        if (i == 0){
                            // 请假第一天，判断开始时间在打卡规则的哪个时间段
                            if (DateUtils.compareToTime(newStartTime,forenoonCheckoutTime) < 0){
                                // 在上午签退时间前，上午状态设为请假
                                newAttendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                                // 下午状态设置为请假
                                newAttendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                            }else if (DateUtils.compareToTime(newStartTime,forenoonCheckoutTime) >= 0 &&
                                    DateUtils.compareToTime(newStartTime,afternoonCheckoutTime)< 0){
                                // 在上午签退时间后，在下午签退时间前，下午状态设置为请假
                                newAttendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                            }
                        }else if (i != daysBetween){
                            // 请假不是第一天，也不是最后一天
                            // 上下午状态都设置请假
                            newAttendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                            newAttendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                        }else {
                            // 请假的最后一天，判断结束时间在打卡规则哪个时间段
                            if (DateUtils.compareToTime(newStopTime,afternoonCheckinTime) < 0){
                                // 在下午签到前
                                // 设置上午状态请假
                                newAttendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                            }else if (DateUtils.compareToTime(newStopTime,afternoonCheckinTime) >= 0 &&
                                    DateUtils.compareToTime(newStopTime,afternoonCheckoutTime) < 0){
                                // 在下午签到后，下午签退前
                                // 设置上午状态请假
                                newAttendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                                // 设置下午状态请假
                                newAttendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                            }
                        }
                        // 打卡类型为普通
                        newAttendance.setClockinType(0);
                        newAttendance.setCreateTime(new Date());
                        newAttendance.setUpdateTime(new Date());
                        newAttendanceList.add(newAttendance);
                    }
                    // 插入数据
                    attendanceMapper.insertBatch(newAttendanceList);
                }else {
                    // 请一天假
                    Attendance newAttendance = new Attendance();
                    newAttendance.setId(UUID.randomUUID().toString().replaceAll("-",""));
                    newAttendance.setEmployeeId(attendanceApply.getApplicantId());
                    newAttendance.setClockinRuleId(clockinRule.getId());
                    newAttendance.setClockingDate(startTime);
                    if (DateUtils.compareToTime(newStartTime,forenoonCheckoutTime) < 0 &&
                            DateUtils.compareToTime(newStopTime,afternoonCheckinTime) < 0){
                        // 开始时间在上午签退之前，结束时间在下午签到之前
                        // 上午状态为请假
                        newAttendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                    }else if (DateUtils.compareToTime(newStartTime,forenoonCheckoutTime) < 0 &&
                            DateUtils.compareToTime(newStopTime,afternoonCheckinTime) > 0){
                        // 开始时间在上午签退之前，结束时间在下午签到之后
                        // 上午状态为请假，下午状态为请假
                        newAttendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                        newAttendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                    }else if (DateUtils.compareToTime(newStartTime,forenoonCheckoutTime) >= 0 &&
                            DateUtils.compareToTime(newStopTime,afternoonCheckinTime) > 0){
                        // 开始时间在上午签退之后，结束时间在下午签到之后
                        // 下午状态为请假
                        newAttendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                    }
                    newAttendance.setClockinType(0);
                    newAttendance.setCreateTime(new Date());
                    newAttendance.setUpdateTime(new Date());
                    // 插入数据
                    attendanceMapper.insert(newAttendance);
                }
            }else {
                // 有出勤记录
                // 获取出勤记录打卡规则
                List<String> clockinRuleIdList = attendanceList.stream().map(Attendance::getClockinRuleId).collect(Collectors.toList());
                List<ClockinRule> clockinRuleList = clockinRuleMapper.selectBatchByIdWithDelete(clockinRuleIdList);
                Map<String, ClockinRule> clockinRuleMap = clockinRuleList.stream().collect(Collectors.toMap(ClockinRule::getId, Function.identity()));
                List<Attendance> updateAttendance = new ArrayList<>();
                for (Attendance attendance : attendanceList){
                    ClockinRule rule = clockinRuleMap.get(attendance.getClockinRuleId());
                    if (DateUtils.compareToTime(newStartTime,rule.getForenoonCheckoutTime()) < 0 &&
                            DateUtils.compareToTime(newStopTime,rule.getAfternoonCheckinTime()) < 0){
                        // 开始时间在上午签退之前，结束时间在下午签到之前
                        // 上午状态为请假
                        attendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                    }else if (DateUtils.compareToTime(newStartTime,rule.getForenoonCheckoutTime()) < 0 &&
                            DateUtils.compareToTime(newStopTime,rule.getAfternoonCheckinTime()) > 0){
                        // 开始时间在上午签退之前，结束时间在下午签到之后
                        // 上午状态为请假，下午状态为请假
                        attendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                        attendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                    }else if (DateUtils.compareToTime(newStartTime,rule.getForenoonCheckoutTime()) >= 0 &&
                            DateUtils.compareToTime(newStopTime,rule.getAfternoonCheckinTime()) > 0){
                        // 开始时间在上午签退之后，结束时间在下午签到之后
                        // 下午状态为请假
                        attendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                    }
                    updateAttendance.add(attendance);
                }
                // 更新数据
                attendanceMapper.updateBatchById(updateAttendance);
                long daysBetween = ChronoUnit.DAYS.between(startTime, stopTime);
                long index = daysBetween-attendanceList.size();
                // 剩下没有出勤记录
                List<Attendance> newAttendanceList = new ArrayList<>();
                for (int i = 0; i < index; i++) {
                    LocalDate clockingDate = startTime.plusDays(attendanceList.size()+i);
                    Attendance newAttendance = new Attendance();
                    newAttendance.setId(UUID.randomUUID().toString().replaceAll("-", ""));
                    newAttendance.setEmployeeId(attendanceApply.getApplicantId());
                    newAttendance.setClockinRuleId(clockinRule.getId());
                    newAttendance.setClockingDate(clockingDate);
                    if (attendanceList.size() == 0 && i == 0){
                        // 请假第一天，判断开始时间在打卡规则的哪个时间段
                        if (DateUtils.compareToTime(newStartTime,forenoonCheckoutTime) < 0){
                            // 在上午签退时间前，上午状态设为请假
                            newAttendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                            // 下午状态设置为请假
                            newAttendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                        }else if (DateUtils.compareToTime(newStartTime,forenoonCheckoutTime) >= 0 &&
                                DateUtils.compareToTime(newStartTime,afternoonCheckoutTime) < 0){
                            // 在上午签退时间后，在下午签退时间前，下午状态设置为请假
                            newAttendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                        }
                    }else if (i != index){
                        // 请假不是第一天，也不是最后一天
                        // 上下午状态都设置请假
                        newAttendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                        newAttendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                    }else {
                        // 请假的最后一天，判断结束时间在打卡规则哪个时间段
                        if (DateUtils.compareToTime(newStopTime,afternoonCheckinTime) < 0){
                            // 在下午签到前
                            // 设置上午状态请假
                            newAttendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                        }else if (DateUtils.compareToTime(newStopTime,afternoonCheckinTime) >= 0 &&
                                DateUtils.compareToTime(newStopTime,afternoonCheckoutTime) < 0){
                            // 在下午签到后，下午签退前
                            // 设置上午状态请假
                            newAttendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                            // 设置下午状态请假
                            newAttendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE.getCodeInt());
                        }
                    }
                    // 打卡类型为普通
                    newAttendance.setClockinType(0);
                    newAttendance.setCreateTime(new Date());
                    newAttendance.setUpdateTime(new Date());
                    newAttendanceList.add(newAttendance);
                }
                // 插入数据
                if (newAttendanceList.size() > 0){
                    attendanceMapper.insertBatch(newAttendanceList);
                }
            }
        }else if (attendanceApply.getApplyType() == 1 && attendanceApply.getApprovalStatus() == 1){
            // 补卡
            // 获取出勤记录
            Attendance attendance = attendanceMapper.selectById(attendanceApply.getAttendanceId());
            // 获取打卡规则
            ClockinRule clockinRule = clockinRuleMapper.selectById(attendance.getClockinRuleId());
            Time forenoonCheckinTime = Time.valueOf(clockinRule.getForenoonCheckinTime().toLocalTime().plusMinutes(1));
            Time forenoonCheckoutTime = clockinRule.getForenoonCheckoutTime();
            Time afternoonCheckinTime = Time.valueOf(clockinRule.getAfternoonCheckinTime().toLocalTime().plusMinutes(1));
            Time afternoonCheckoutTime = clockinRule.getAfternoonCheckoutTime();
            if (attendanceApply.getAttendanceLot() == 0){
                // 补上午签到卡
                attendance.setForenoonCheckinTime(clockinRule.getForenoonCheckinTime());
            }else if (attendanceApply.getAttendanceLot() == 1){
                // 补上午签退卡
                attendance.setForenoonCheckoutTime(clockinRule.getForenoonCheckoutTime());
            }else if (attendanceApply.getAttendanceLot() == 2){
                // 补下午签到卡
                attendance.setAfternoonCheckinTime(clockinRule.getAfternoonCheckinTime());
            }else if (attendanceApply.getAttendanceLot() == 3){
                // 补下午签退卡
                attendance.setAfternoonCheckoutTime(clockinRule.getAfternoonCheckoutTime());
            }
            // 重新设置状态
            // 补卡日期为今天
//            if (attendance.getClockingDate().equals(LocalDate.now())){
//                if (attendanceApply.getAttendanceLot() == 0 || attendanceApply.getAttendanceLot() == 1){
//                    // 上午
//
//                }else {
//                    // 下午
//
//                }
//            }
            // 上午
            if (attendance.getForenoonCheckinTime() != null){
                if (DateUtils.compareToTime(attendance.getForenoonCheckinTime(),forenoonCheckinTime) <= 0 &&
                        attendance.getForenoonCheckoutTime() == null){
                    // 未签退
                    attendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_Not_signed_out.getCodeInt());
                }else if (DateUtils.compareToTime(attendance.getForenoonCheckinTime(),forenoonCheckinTime) > 0 &&
                        attendance.getForenoonCheckoutTime() == null){
                    // 迟到&未签退
                    attendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_BE_LATE_Not_signed_out.getCodeInt());
                }else if (DateUtils.compareToTime(attendance.getForenoonCheckinTime(),forenoonCheckinTime) <= 0 &&
                        DateUtils.compareToTime(attendance.getForenoonCheckoutTime(),forenoonCheckoutTime) >= 0){
                    // 正常
                    attendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_NORMAL.getCodeInt());
                }else if (DateUtils.compareToTime(attendance.getForenoonCheckinTime(),forenoonCheckinTime) > 0 &&
                        DateUtils.compareToTime(attendance.getForenoonCheckoutTime(),forenoonCheckoutTime) >= 0){
                    // 迟到
                    attendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_BE_LATE.getCodeInt());
                }else if (DateUtils.compareToTime(attendance.getForenoonCheckinTime(),forenoonCheckinTime) > 0 &&
                        DateUtils.compareToTime(attendance.getForenoonCheckoutTime(),forenoonCheckoutTime) < 0){
                    // 迟到&早退
                    attendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_BE_LATE_LEAVE_EARLY.getCodeInt());
                }else if (DateUtils.compareToTime(attendance.getForenoonCheckinTime(),forenoonCheckinTime) <= 0 &&
                        DateUtils.compareToTime(attendance.getForenoonCheckoutTime(),forenoonCheckoutTime) < 0){
                    // 早退
                    attendance.setForenoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE_EARLY.getCodeInt());
                }
            }
            if (attendance.getAfternoonCheckinTime() != null){
                // 下午
                if (DateUtils.compareToTime(attendance.getAfternoonCheckinTime(),afternoonCheckinTime) <= 0 &&
                        attendance.getAfternoonCheckoutTime() == null){
                    // 未签退
                    attendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_Not_signed_out.getCodeInt());
                }else if (DateUtils.compareToTime(attendance.getAfternoonCheckinTime(),afternoonCheckinTime) > 0 &&
                        attendance.getAfternoonCheckoutTime() == null){
                    // 迟到&未签退
                    attendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_BE_LATE_Not_signed_out.getCodeInt());
                }else if (DateUtils.compareToTime(attendance.getAfternoonCheckinTime(),afternoonCheckinTime)  <= 0 &&
                        DateUtils.compareToTime(attendance.getAfternoonCheckoutTime(),afternoonCheckoutTime) >= 0){
                    // 正常
                    attendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_NORMAL.getCodeInt());
                }else if (DateUtils.compareToTime(attendance.getAfternoonCheckinTime(),afternoonCheckinTime) > 0 &&
                        DateUtils.compareToTime(attendance.getAfternoonCheckoutTime(),afternoonCheckoutTime) >= 0){
                    // 迟到
                    attendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_BE_LATE.getCodeInt());
                }else if (DateUtils.compareToTime(attendance.getAfternoonCheckinTime(),afternoonCheckinTime) > 0 &&
                        DateUtils.compareToTime(attendance.getAfternoonCheckoutTime(),afternoonCheckoutTime) < 0){
                    // 迟到&早退
                    attendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_BE_LATE_LEAVE_EARLY.getCodeInt());
                }else if (DateUtils.compareToTime(attendance.getAfternoonCheckinTime(),afternoonCheckinTime) <= 0 &&
                        DateUtils.compareToTime(attendance.getAfternoonCheckoutTime(),afternoonCheckoutTime) < 0){
                    // 早退
                    attendance.setAfternoonStatus(BaseEnum.CLOCKIN_STATUS_LEAVE_EARLY.getCodeInt());
                }
            }
            // 更新数据
            attendanceMapper.updateById(attendance);
        }else if ((attendanceApply.getApplyType() == 2 || attendanceApply.getApplyType() == 3) && attendanceApply.getApprovalStatus() == 1){
            // 1外勤，2出差
            int clockinType = attendanceApply.getApplyType() == 2 ? 1 : 2;
            // 获取开始日期
            LocalDate startTime = attendanceApply.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            // 获取结束日期
            LocalDate stopTime = attendanceApply.getStopTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            // 查出勤记录
            // 获取开始日期到结束日期的出勤记录
            LambdaQueryWrapper<Attendance> attendanceLQW = new LambdaQueryWrapper<>();
            attendanceLQW.eq(Attendance::getEmployeeId,attendanceApply.getApplicantId())
                    .between(Attendance::getClockingDate,startTime,stopTime);
            List<Attendance> attendanceList = attendanceMapper.selectList(attendanceLQW);
            // 获取打卡规则
            // 先查询个人打卡规则
            LambdaQueryWrapper<ClockinRule> clockinRuleLQW = new LambdaQueryWrapper<>();
            clockinRuleLQW.eq(ClockinRule::getEmployeeId,attendanceApply.getApplicantId())
                    .eq(ClockinRule::getRuleType,0);
            ClockinRule clockinRule = clockinRuleMapper.selectOne(clockinRuleLQW);
            // 没有个人打卡规则，就查询部门打卡规则
            if (clockinRule == null || clockinRule.getForenoonCheckinTime() == null){
                LambdaQueryWrapper<EmployeeDepartment> employeeDepartmentLQW = new LambdaQueryWrapper<>();
                employeeDepartmentLQW.eq(EmployeeDepartment::getEmployeeId,attendanceApply.getApplicantId());
                List<EmployeeDepartment> employeeDepartmentList = employeeDepartmentMapper.selectList(employeeDepartmentLQW);
                if (employeeDepartmentList.size() == 1){
                    clockinRule = getDepartmentClockinRule(employeeDepartmentList.get(0).getDepartmentId());
                }else{
                    String msg = "请联系管理员设置个人打卡规则";
                    return new Result(HttpStatus.NO_CONTENT,msg);
                }
            }
            if (clockinRule == null){
                String msg = "请联系管理员设置个人打卡规则";
                return new Result(HttpStatus.NO_CONTENT,msg);
            }
            if (attendanceList.isEmpty()){
                // 没有记录。新建
                long daysBetween = ChronoUnit.DAYS.between(startTime, stopTime);
                if (daysBetween > 0){
                    // 多天
                    List<Attendance> newAttendanceList = new ArrayList<>();
                    for (int i = 0; i <= daysBetween; i++) {
                        LocalDate clockingDate = startTime.plusDays(i);
                        Attendance newAttendance = new Attendance();
                        newAttendance.setId(UUID.randomUUID().toString().replaceAll("-", ""));
                        newAttendance.setEmployeeId(attendanceApply.getApplicantId());
                        newAttendance.setClockinRuleId(clockinRule.getId());
                        newAttendance.setClockingDate(clockingDate);
                        // 打卡类型
                        newAttendance.setClockinType(clockinType);
                        newAttendance.setCreateTime(new Date());
                        newAttendance.setUpdateTime(new Date());
                        newAttendanceList.add(newAttendance);
                    }
                    // 插入数据
                    attendanceMapper.insertBatch(newAttendanceList);
                }else {
                    // 一天
                    Attendance newAttendance = new Attendance();
                    newAttendance.setId(UUID.randomUUID().toString().replaceAll("-",""));
                    newAttendance.setEmployeeId(attendanceApply.getApplicantId());
                    newAttendance.setClockinRuleId(clockinRule.getId());
                    newAttendance.setClockingDate(startTime);
                    newAttendance.setClockinType(clockinType);
                    newAttendance.setCreateTime(new Date());
                    newAttendance.setUpdateTime(new Date());
                    // 插入数据
                    attendanceMapper.insert(newAttendance);
                }
            }else {
                // 有出勤记录
                for (Attendance attendance : attendanceList){
                    attendance.setClockinType(clockinType);
                }
                attendanceMapper.updateBatchById(attendanceList);
                long daysBetween = ChronoUnit.DAYS.between(startTime, stopTime);
                long index = daysBetween-attendanceList.size();
                // 剩下没有出勤记录
                List<Attendance> newAttendanceList = new ArrayList<>();
                for (int i = 0; i < index; i++) {
                    LocalDate clockingDate = startTime.plusDays(attendanceList.size()+i);
                    Attendance newAttendance = new Attendance();
                    newAttendance.setId(UUID.randomUUID().toString().replaceAll("-", ""));
                    newAttendance.setEmployeeId(attendanceApply.getApplicantId());
                    newAttendance.setClockinRuleId(clockinRule.getId());
                    newAttendance.setClockingDate(clockingDate);
                    // 打卡类型
                    newAttendance.setClockinType(clockinType);
                    newAttendance.setCreateTime(new Date());
                    newAttendance.setUpdateTime(new Date());
                    newAttendanceList.add(newAttendance);
                }
                // 插入数据
                if (newAttendanceList.size() > 0){
                    attendanceMapper.insertBatch(newAttendanceList);
                }
            }
        }
        attendanceApplyMapper.updateById(attendanceApply);
        // 创建消息通知
        MessageNotification msg = new MessageNotification();
        // 标题
        String applyType;
        String approvalStatus;
        if (attendanceApply.getApprovalStatus() == 1){
            // 申请通过
            approvalStatus = "已通过";
        }else {
            // 申请未通过
            approvalStatus = "未通过";
        }
        if (attendanceApply.getApplyType() == 0){
            // 请假
            applyType = "请假";
        }else if (attendanceApply.getApplyType() == 1){
            // 补卡
            applyType = "补卡";
        }else if (attendanceApply.getApplyType() == 2){
            // 外勤
            applyType = "外勤";
        }else {
            // 出差
            applyType = "出差";
        }
        String title = applyType+"申请"+approvalStatus;
        msg.setTitle(title);
        // 内容
        String content = "您的"+applyType+"申请"+approvalStatus;
        msg.setContent(content);
        // 发布人为审核人
        msg.setPublisherId(user.getId());
        // 接收人为申请人
        msg.setReceiverId(attendanceApply.getApplicantId());
        // 关联id
        msg.setRelevanceId(attendanceApply.getId());
        // 类型为员工考勤
        msg.setMsgType(3);
        // 子菜单为5考勤审批
        msg.setSubmenu(5);
        // 状态为待处理
        msg.setStatus(0);
        msgNotificationMapper.insert(msg);
        return Result.success("操作成功");
    }

    /**
     * 递归获取部门打卡规则
     * @param departmentId 部门id
     * @return 打卡规则对象
     */
    public ClockinRule getDepartmentClockinRule(String departmentId){
        LambdaQueryWrapper<ClockinRule> clockinRuleLQW = new LambdaQueryWrapper<>();
        clockinRuleLQW.eq(ClockinRule::getDepartmentId,departmentId)
                .eq(ClockinRule::getRuleType,1);
        ClockinRule clockinRule = clockinRuleMapper.selectOne(clockinRuleLQW);
        if (clockinRule != null && clockinRule.getForenoonCheckinTime() != null){
            return clockinRule;
        }
        Department department = departmentMapper.selectById(departmentId);
        if (department.getParentDepartmentId() == null){
            return null;
        }else{
            return getDepartmentClockinRule(department.getParentDepartmentId());
        }
    }

}
